﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;
using HeapSpecSupport;
using SimpleIR;

namespace Evaluator.EvalState
{
	/// <summary>
	/// Structure representing an entry on the local evaluation stack.
	/// </summary>
	internal struct EvalStackEntry
	{
		/// <summary>
		/// The type of the value on the eval stack -- also indicates which of the other fields is valid.
		/// </summary>
		private readonly IRType m_sloctype;

		/// <summary>
		/// If this is a numeric stack entry the value is in this field.
		/// </summary>
		private readonly EValue m_refandmemoryvalues;

		/// <summary>
		/// If this is a function pointer stack entry the value is in this field.
		/// </summary>
		private readonly IRInvokeIdentity m_funcptrvalue;

		/// <summary>
		/// If this is a struct stack entry the value is in this field.
		/// </summary>
		private readonly BAPLayoutMap m_structvalue;

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{
			Contract.Invariant(this.m_sloctype != null);
			Contract.Invariant(this.m_structvalue != null);
		}

		private EvalStackEntry(IRType stype, EValue vval, IRInvokeIdentity fptr, BAPLayoutMap svals)
		{
			this.m_sloctype = stype;
			this.m_refandmemoryvalues = vval;
			this.m_funcptrvalue = fptr;
			this.m_structvalue = svals;
		}

		internal EvalStackEntry CopyStackEntry()
		{
			BAPLayoutMap sval = (this.m_structvalue != null) ? this.m_structvalue.CopyBapLayout() : null;
			return new EvalStackEntry(this.m_sloctype, this.m_refandmemoryvalues, this.m_funcptrvalue, sval); 
		}

		internal static EvalStackEntry MakeValueStackEntry(EValue numv)
		{
			Contract.Requires(numv != null);
			
			return new EvalStackEntry(numv.TypeOfValue, numv, null, null);
		}

		internal static EvalStackEntry MakeFunctionPtrStackEntry(IRFunctionPointerType stype, IRInvokeIdentity fptr)
		{
			Contract.Requires(stype != null);
			Contract.Requires(fptr != null);

			return new EvalStackEntry(stype, null, fptr, null);
		}

		internal static EvalStackEntry MakeStructStackEntry(IRStructType stype, BAPLayoutMap svals)
		{
			Contract.Requires(stype != null);
			Contract.Requires(svals != null);

			return new EvalStackEntry(stype, null, null, svals);
		}

		internal EvalStackEntry RetypeAtCall(IRType tt)
		{
			if(tt is IRPtrType)
			{
				Contract.Assert(IRProgram.IsT1AssignableToT2(this.m_sloctype, tt));

				IRPtrType pty = (IRPtrType)tt;
				EPointer cptr = (EPointer) this.m_refandmemoryvalues;
				return new EvalStackEntry(pty, cptr.ConvertPointerType(pty), null, null);
			}
			else
				return this;
		}

		public override string ToString()
		{
			if(this.m_sloctype is IRNumericType || this.m_sloctype is IRReferenceType || this.m_sloctype is IRPtrType)
				return this.m_sloctype.ToString() + " " + this.m_refandmemoryvalues.ToString();
			else if(this.m_sloctype is IRFunctionPointerType)
				return this.m_sloctype.ToString() + " " + this.m_funcptrvalue.ToString();
			else
				return this.m_structvalue.ToString();
		}

		internal IRType SLocType
		{ get { return this.m_sloctype; } }

		internal ENumeric NumericValue
		{
			get
			{
				Contract.Requires(this.SLocType is IRNumericType);

				return (ENumeric)this.m_refandmemoryvalues;
			}
		}

		internal IRInvokeIdentity FuncPtrValue
		{
			get
			{
				Contract.Requires(this.SLocType is IRFunctionPointerType);

				return this.m_funcptrvalue;
			}
		}

		internal EPointer PointerValue
		{
			get
			{
				Contract.Requires(this.SLocType is IRPtrType);

				return (EPointer)this.m_refandmemoryvalues;
			}
		}

		internal EReference ReferenceValue
		{
			get
			{
				Contract.Requires(this.SLocType is IRReferenceType);

				return (EReference)this.m_refandmemoryvalues;
			}
		}

		internal BAPLayoutMap StructValue
		{
			get
			{
				Contract.Requires(this.SLocType is IRStructType);

				return this.m_structvalue;
			}
		}
	}
}
